﻿using System;
using System.Collections;
using System.Globalization;
using System.Text;
using Lextm.SharpSnmpLib;
using Snmp.Agent.DataLoader;

namespace Snmp.Agent.MplsLsrMib
{
    ////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>   Table. </summary>
	///
	/// <remarks>   Gospo, 2012-12-30. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	internal class Table
	{
		/// <summary>   Identifier for the entry. </summary>
		private const string EntryId = ".1";

		/// <summary>   The rows. </summary>
		private readonly SortedList _rows = new SortedList(new IdStringComparer());

		/// <summary>   Number of attributes. </summary>
		private readonly int _attributesCount;

		/// <summary>   Zero-based index of the index attribute. </summary>
        private readonly int[] _indexAttrsIdxs;

		/// <summary>   Identifier for the table. </summary>
		private readonly string _tableId;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>   Constructor. </summary>
		///
		/// <remarks>   Gospo, 2012-12-30. </remarks>
		///
		/// <param name="attributesCount">  Number of attributes. </param>
		/// <param name="indexAttrIdx">     Zero-based index of the index attribute. </param>
		/// <param name="tableId">          Identifier for the table. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Table(int attributesCount, int[] indexAttrsIdxs, string tableId)
		{
			
			_attributesCount = attributesCount;
            _indexAttrsIdxs = indexAttrsIdxs;
			_tableId = tableId;
		}

	    /// <summary>   Identifier for the table. </summary>
	    public string TableId
	    {
	        get { return _tableId; }
	    }

	    ////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>   Request to get a particular variable. </summary>
		///
		/// <remarks>   Gospo, 2012-12-30. </remarks>
		///
		/// <param name="relativeId">   Relative identifier. </param>
		///
		/// <returns>   The variable. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Variable GetRequest(String relativeId)
		{
			Variable result;

			int attrIdx;
			string rowId;
			ParseRelVarId(relativeId, out attrIdx, out rowId);

			if (_rows.ContainsKey(rowId))
			{
				var row = (ArrayList) _rows[rowId];
				var data = (ISnmpData) row[attrIdx];
				result = new Variable(new ObjectIdentifier(TableId + relativeId), data);
			}
			else
			{
				result = new Variable(new ObjectIdentifier(TableId + relativeId), new NoSuchObject());
			}

			return result;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>   GetNextRequest </summary>
		///
		/// <remarks>   Gospo, 2012-12-30. </remarks>
		///
		/// <param name="relativeId">   Relative identifier. </param>
		///
		/// <returns>   The next variable. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Variable GetNextRequest(String relativeId)
		{
			Variable result;

			var idElems = relativeId.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
			var idElemsCount = idElems.Length;
			if (idElemsCount < 2)
			{
				// entry request is equivalent to table request
				result = TableGetNextRequest();
			}
			else if (idElemsCount == 2)
			{
				// column request
				var attrId = idElems[1];
				var attrIdx = int.Parse(attrId) - 1;
				result = ColumnGetNextRequest(attrIdx);
			}
			else
			{
				// variable request
				result = VariableGetNextRequest(relativeId);
			}

			return result;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>   GetNextRequest invoked at the table object. </summary>
		///
		/// <remarks>   Gospo, 2012-12-30. </remarks>
		///
		/// <returns>   The first variable in the table. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////
		
        private Variable TableGetNextRequest()
		{
			Variable result;
			if (_rows.Count > 0)
			{
				const int attrIdx = 0;
				const int rowIdx = 0;

				var row = (ArrayList) _rows.GetByIndex(rowIdx);
				var data = (ISnmpData) row[attrIdx];
                var attrId = "." + (attrIdx + 1).ToString(CultureInfo.InvariantCulture);
                var rowId =  _rows.GetKey(rowIdx);
				var relativeId = EntryId + attrId + rowId;
				result = new Variable(new ObjectIdentifier(TableId + relativeId), data);
			}
			else
			{
				// todo next table
				result = null;
			}

			return result;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>   GetNextRequest invoked at a column object. </summary>
		///
		/// <remarks>   Gospo, 2012-12-30. </remarks>
		///
		/// <param name="attrIdx">  Zero-based index of the attribute. </param>
		///
		/// <returns>   The first variable in the column. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////
	
        private Variable ColumnGetNextRequest(int attrIdx)
		{
			Variable result;

			if (_rows.Count > 0)
			{
				const int rowIdx = 0;
				var row = (ArrayList) _rows.GetByIndex(rowIdx);
				var data = (ISnmpData) row[attrIdx];
				var attrId = "." + (attrIdx + 1).ToString(CultureInfo.InvariantCulture);
                var rowId = _rows.GetKey(rowIdx);
				var relativeId = EntryId + attrId + rowId;
				result = new Variable(new ObjectIdentifier(TableId + relativeId), data);
			}
			else
			{
				// todo next table
				result = null;
			}

			return result;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>   GetNextRequest invoked at a variable. </summary>
		///
		/// <remarks>   Gospo, 2012-12-30. </remarks>
		///
		/// <param name="relativeId">   Relative identifier. </param>
		///
		/// <returns>   . </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////
		
        private Variable VariableGetNextRequest(String relativeId)
		{
			int attrIdx;
			string rowId;
			ParseRelVarId(relativeId, out attrIdx, out rowId);

			Variable result;

			if (_rows.ContainsKey(rowId))
			{
				var rowIdx = _rows.IndexOfKey(rowId);
				var nextRowIdx = rowIdx + 1;
				if (_rows.Count > nextRowIdx)
				{
					var nextRow = (ArrayList) _rows.GetByIndex(nextRowIdx);
					var data = (ISnmpData) nextRow[attrIdx];
                    var attrId = "." + (attrIdx + 1).ToString(CultureInfo.InvariantCulture);
                    var nextRowId = _rows.GetKey(nextRowIdx);
					var nextRelativeId = EntryId +  attrId + nextRowId;
					result = new Variable(new ObjectIdentifier(TableId + nextRelativeId), data);
				}
				else if (attrIdx + 1 < _attributesCount)
				{
					var nextAttrIdx = attrIdx + 1;
					result = ColumnGetNextRequest(nextAttrIdx);
				}
				else
				{
					// todo next table
					result = null;
				}
			}
			else
			{
				result = new Variable(new ObjectIdentifier(TableId + relativeId), new NoSuchObject());
			}

			return result;
		}

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   SetRequest invoked at a variable. </summary>
        ///
        /// <remarks>   Gospo, 2013-01-12. </remarks>
        ///
        /// <param name="relativeId">   Relative identifier. </param>
        /// <param name="value">        Value. </param>
        ////////////////////////////////////////////////////////////////////////////////////////////////////

        public Variable SetRequest(string relativeId, ISnmpData value)
        {
            Variable result;

            int attrIdx;
            string rowId;
            ParseRelVarId(relativeId, out attrIdx, out rowId);

            if (_rows.ContainsKey(rowId))
            {
                var row = (ArrayList)_rows[rowId];
                row[attrIdx] = value;
                result = new Variable(new ObjectIdentifier(TableId + relativeId), value);
            }
            else
            {
                result = new Variable(new ObjectIdentifier(TableId + relativeId), new NoSuchObject());
            }

            return result;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Parse relative variable identifier. </summary>
        ///
        /// <remarks>   Gospo, 2012-12-30. </remarks>
        ///
        /// <param name="relativeId">   Relative identifier. </param>
        /// <param name="attrIdx">      [out] Zero-based index of the attribute. </param>
        /// <param name="rowId">        [out] Identifier for the row. </param>
        ////////////////////////////////////////////////////////////////////////////////////////////////////
	
		private static void ParseRelVarId(String relativeId, out int attrIdx, out string rowId)
		{
			//id format: .EntryId.AttributeId.RowId
			// EntryId const 1
			var idElems = relativeId.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
			if(idElems.Length < 3)
			{
				throw new IdParsingException(relativeId);
			}
            attrIdx = int.Parse(idElems[1]) - 1; // 0-based
            rowId = "";
            for (int i = 2; i < idElems.Length; i++)
            {
                rowId += "." + idElems[i];
            }
		}

        private class IdParsingException : Exception
		{
			public string RelativeId { get; private set; }

			public IdParsingException(string badString)
			{
				RelativeId = badString;
			} 
		}

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>   Load data from a MIB Table. </summary>
        ///
        /// <remarks>   Gospo, 2012-12-30. </remarks>
        ///
        /// <param name="mibTable">   MIB Table containing data. </param>
        ////////////////////////////////////////////////////////////////////////////////////////////////////
       
        public void Load(MibTable mibTable)
        {
            _rows.Clear();
            ArrayList rows = mibTable.Rows;
            foreach (ArrayList rawRow in rows)
            {
                StringBuilder id = new StringBuilder();
                ArrayList row = new ArrayList();
                foreach (var i in _indexAttrsIdxs)
                {
                    id .Append( "." + rawRow[i].ToString().Substring(1));
                }

                foreach (string typedVal in rawRow)
                {
                    string type = typedVal.Substring(0, 1);
                    string val = typedVal.Substring(1);
                    if(type.Equals(DataTypeConverter.GetDataTypePrefix(DataType.Integer32)))
                    {
                        row.Add(new Integer32(int.Parse(val)));
                    }
                    else if (type.Equals(DataTypeConverter.GetDataTypePrefix(DataType.OctetString)))
                    {
                        row.Add(new OctetString(val));
                    }
                    else if (type.Equals(DataTypeConverter.GetDataTypePrefix(DataType.ObjectIdentifier)))
                    {
                        row.Add(new ObjectIdentifier(val));
                    }
                }
                _rows.Add(id.ToString(), row);
            }
        }
	}
}
